package top.wuchu.rpc.transport;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.wuchu.rpc.entity.RpcRequest;
import top.wuchu.rpc.entity.RpcResponse;
import top.wuchu.rpc.transport.netty.client.NettyClient;
import top.wuchu.rpc.transport.socket.client.SocketClient;
import top.wuchu.rpc.util.RpcMessageChecker;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * RPC客户端动态代理，为啥要用一个rpc代理呢，是因为用户只管调用服务，
 * 至于调用服务、发送网络请求这些操作交给RPC代理去完成。用户只需要调用服务接口，然后获取返回数据就行。
 * 采用JDK动态代理的方式进行rpc代理。
 *
 * @author WuChu
 */
public class RpcClientProxy implements InvocationHandler {

    private static final Logger logger = LoggerFactory.getLogger(RpcClientProxy.class);

    private final RpcClient client;

    public RpcClientProxy(RpcClient client) {
        this.client = client;
    }

    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this);
    }

    /**
     * 通过动态代理的方式
     * 代理对象执行什么方法，就会进入invoke这个方法
     * proxy: 代理对象
     * method: 执行的方法
     * args: 方法的参数
     * @return 返回远程服务那个方法的调用结果
     */
    @SuppressWarnings("unchecked")
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {

        logger.info("调用方法: {}#{}", method.getDeclaringClass().getName(), method.getName());
        // RpcClientProxy调用服务接口后，来到此处，method就是服务的方法
        // 于是我们就要封装 请求对象rpcRequest, 主要参数有：
        // 1、请求ID : UUID.randomUUID().toString()
        // 2、服务名称 ： method.getDeclaringClass().getName()
        RpcRequest rpcRequest = new RpcRequest(UUID.randomUUID().toString(),
                                               method.getDeclaringClass().getName(),
                                               method.getName(),
                                               args,
                                               method.getParameterTypes(),
                                              false);
        RpcResponse rpcResponse = null;

        // 判断使用哪种通信方式
        if (client instanceof NettyClient) {
            try {
                CompletableFuture<RpcResponse> completableFuture = (CompletableFuture<RpcResponse>) client.sendRequest(rpcRequest);
                rpcResponse = completableFuture.get();
            } catch (Exception e) {
                logger.error("方法调用请求发送失败", e);
                return null;
            }
        }
        if (client instanceof SocketClient) {
            // 此处通过SocketClient向远程服务进行调用
            rpcResponse = (RpcResponse) client.sendRequest(rpcRequest);
        }

        // 对返回结果进行检查
        RpcMessageChecker.check(rpcRequest, rpcResponse);

        return rpcResponse.getData();
    }
}
